package com.xxg.renrenorder.service;

import com.alibaba.fastjson.JSON;
import com.xxg.renrencommon.domain.filedenum.accountenum.AccountGroupEnum;
import com.xxg.renrencommon.domain.filedenum.orderenum.AfterSalesOrderStatusEnum;
import com.xxg.renrencommon.domain.filedenum.orderenum.OrderStatusEnum;
import com.xxg.renrencommon.domain.filedenum.orderenum.OrderTypeEnum;
import com.xxg.renrencommon.domain.filedenum.orderenum.PaymentTypeEnum;
import com.xxg.renrencommon.domain.result.BaseResult;
import com.xxg.renrencommon.exception.OrderManagerException;
import com.xxg.renrencommon.exception.exceptionenum.OrderExceptionCodeCodeEnum;
import com.xxg.renrenorder.component.UserBenefitComponent;
import com.xxg.renrenorder.dao.AfterSalesOrderInfoDao;
import com.xxg.renrenorder.dao.OrderMasterInfoDao;
import com.xxg.renrenorder.dao.bean.AfterSalesOrderInfo;
import com.xxg.renrenorder.dao.bean.OrderMasterInfo;
import com.xxg.renrenorder.dao.bean.PaymentInfo;
import com.xxg.renrenorder.dao.bean.ProductInfo;
import com.xxg.renrenorder.domain.query.ModQuery;
import com.xxg.renrenorder.domain.result.ReFundResult;
import com.xxg.renrenorder.manager.LpWeChatPayManager;
import com.xxg.renrenorder.util.RedisUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 类说明
 *
 * @author szj
 * @date 2021/12/14
 */
@Service
@Slf4j
@AllArgsConstructor
public class PayServiceImpl implements PayService{
    private final LpWeChatPayManager lpWeChatPayManager;
    private final OrderMasterInfoDao orderMasterInfoDao;
    private final AfterSalesOrderInfoDao afterSalesOrderInfoDao;
    private final UserBenefitComponent userBenefitComponent;
    private final RedisUtil redisUtil;
    private final static String TRADE_CLOSED = "TRADE_CLOSED";
    private final static String TRADE_SUCCESS = "TRADE_SUCCESS";
    private final static String TRADE_FINISHED = "TRADE_FINISHED";

    @Override
    public BaseResult<Map<String, String>> wechatpayOrder(String orderId, int paymentType, String openId) {
        String totalAmount, body;
        OrderMasterInfo orderMasterInfo = getOrderTotalPay(orderId);
        if (orderMasterInfo == null) {
            totalAmount = null;
        } else {
            totalAmount = orderMasterInfo.getTotalPay().setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        }
        body = "淘灯优品-购买商品";
        if (totalAmount == null) {
            return new BaseResult<>(OrderExceptionCodeCodeEnum.ORDER_NOT_EXISTS);
        }
        Map<String, String> resp;
        try {
            if (PaymentTypeEnum.LP_WECHATPAY.match(paymentType)) {
                resp = lpWeChatPayManager.tradePay(body, orderId, totalAmount, openId);
            }else {
                throw new RuntimeException("PaymentType error");
            }
        } catch (OrderManagerException e) {
            return new BaseResult<>(e);
        }
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setPaymentType(paymentType);
        orderMasterInfo.setPaymentInfo(paymentInfo);
        orderMasterInfoDao.updatePayDetail(orderMasterInfo);
        return new BaseResult<>(resp);
    }

    /**
     * 查询订单需要支付的金额
     *
     * @param orderId 订单ID
     * @return 订单详情
     */
    private OrderMasterInfo getOrderTotalPay(String orderId) {
        // 查询订单需要支付的金额
        return orderMasterInfoDao.getOrderByOrderId(orderId);
    }

    @Override
    public String lpWechatpayNotify(String notifyStr) {
        Map<String, String> params = lpWeChatPayManager.payNotify(notifyStr);
        // 收到通知后，对数据进行处理
        if (params != null) {
            // 订单号
            String outTradeNo = params.get("out_trade_no");
            // 微信支付订单号
            String tradeNo = params.get("transaction_id");
            // 交易付款时间
            String gmtPayment = params.get("time_end");
            // 付款金额
            String totalAmount = params.get("total_fee");
            // 交易类型
            Integer tradeType = PaymentTypeEnum.LP_WECHATPAY.getValue();
            if (!checkParams(outTradeNo, tradeNo, gmtPayment, totalAmount)) {
                return "<xml>\n" +
                        "  <return_code><![CDATA[FAIL]]></return_code>\n" +
                        "  <return_msg><![CDATA[参数格式校验错误]]></return_msg>\n" +
                        "</xml>";
            }
            // 付款金额转成元
            totalAmount = ((new BigDecimal(totalAmount)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP)).toString();
            String orderId = outTradeNo;
            log.info("orderId:{}", orderId);
            // 查询订单详情
            OrderMasterInfo orderMasterInfo = orderMasterInfoDao.getOrderByOrderId(orderId);
            // 消息可能会多次发送，这里防止重复处理
            if (orderMasterInfo != null && OrderStatusEnum.UN_PAID.getValue() == orderMasterInfo.getOrderStatus()) {
                // 交易支付成功
                boolean flag = orderMasterInfo.getTotalPay().compareTo(new BigDecimal(totalAmount)) == 0;
                // 订单金额与实际支付金额一致
                orderMasterInfoDao.setOrderPayment(orderId, tradeType, tradeNo, gmtPayment, TRADE_SUCCESS, !flag);
                // 拆分订单
                splitOrder(orderId);
            }
        }
        return "<xml>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>";
    }

    /**
     * 拆分订单
     * @param orderId 订单id
     */
    private void splitOrder(String orderId) {
        OrderMasterInfo info = orderMasterInfoDao.getOrderByOrderId(orderId);
        if (info.getDeleted()) {
            return;
        }
        Integer userGroupId = info.getUserGroupId();
        List<ProductInfo> productInfos = info.getProductInfos();
        List<Integer> shopIds = productInfos.stream().map(ProductInfo::getShopId).distinct().collect(Collectors.toList());
        if (shopIds.size() > 1) {
            // 有多个店铺需要拆
            shopIds.forEach(shopId -> {
                OrderMasterInfo info1 = orderMasterInfoDao.getOrderByOrderId(info.getId() + shopId);
                if (info1 != null) {
                    return;
                }
                List<ProductInfo> collect = productInfos.stream().filter(productInfo -> productInfo.getShopId().equals(shopId)).collect(Collectors.toList());
                OrderMasterInfo orderMasterInfo = new OrderMasterInfo();
                BeanUtils.copyProperties(info, orderMasterInfo);
                orderMasterInfo.setProductInfos(collect);
                orderMasterInfo.setShopId(shopId);
                orderMasterInfo.setId(info.getId() + shopId);
                BigDecimal total = new BigDecimal("0");
                BigDecimal totalPrice = new BigDecimal("0");
                int orderType = OrderTypeEnum.NORMAL.getValue();
                for (ProductInfo productInfo : collect) {
                    if (productInfo.getIsCarryProduct()) {
                        orderType = OrderTypeEnum.CARRY.getValue();
                    }
                    totalPrice = totalPrice.add(productInfo.getSkuPrice().multiply(new BigDecimal(productInfo.getNumber())));
                    total = total.add(productInfo.getPay().multiply(new BigDecimal(productInfo.getNumber())));
                }
                orderMasterInfo.setTotal(total.setScale(2, BigDecimal.ROUND_HALF_UP));
                orderMasterInfo.setTotalPrice(totalPrice.setScale(2, BigDecimal.ROUND_HALF_UP));
                if (AccountGroupEnum.STORE.match(userGroupId) || AccountGroupEnum.SUPPER_DEALER.match(userGroupId)) {
                    orderMasterInfo.setCommission(orderMasterInfo.getTotal().multiply(new BigDecimal(orderMasterInfo.getCommissionInfo().getRatio())));
                }
                orderMasterInfo.setTotalPay(orderMasterInfo.getTotal().subtract(orderMasterInfo.getTotalDiscount()).add(orderMasterInfo.getCommission()));
                orderMasterInfo.setOrderType(orderType);
                orderMasterInfoDao.submitOrder(orderMasterInfo);
                // 收益记录
                userBenefitComponent.recordBenefit(orderMasterInfo);
            });
            // 拆完把原先订单删除
            orderMasterInfoDao.removeOrderById(orderId);
        }else {
            // 不需要拆，把shopId更新到外面
            orderMasterInfoDao.updateShopId(orderId, shopIds.get(0));
            info.setShopId(shopIds.get(0));
            // 收益记录
            userBenefitComponent.recordBenefit(info);
        }

    }

    @Override
    public BaseResult<Void> modPaySuccess(ModQuery query) {
        boolean b = query.checkPassword();
        if (!b) {
            return new BaseResult<>();
        }
        // 查询订单详情
        String orderId = query.getId();
        OrderMasterInfo orderMasterInfo = orderMasterInfoDao.getOrderByOrderId(orderId);
        // 消息可能会多次发送，这里防止重复处理
        if (orderMasterInfo != null && OrderStatusEnum.UN_PAID.getValue() == orderMasterInfo.getOrderStatus()) {
            // 订单金额与实际支付金额一致
            orderMasterInfoDao.setOrderPayment(orderId, PaymentTypeEnum.LP_WECHATPAY.getValue(), "123", "2021-12-23 10:00:00", TRADE_SUCCESS, true);
            // 拆分订单并记录每份收益
            splitOrder(orderId);
        }
        return new BaseResult<>();
    }

    @Override
    public String lpWechatRefundNotify(String notifyStr) {
        Map<String, String> params = lpWeChatPayManager.refundNotify(notifyStr);
        // 收到通知后，对数据进行处理
        if (params != null) {
            // 订单号
            String outTradeNo = params.get("out_trade_no");
            // 售后订单ID
            String afterSalesId = params.get("out_refund_no");
            // 微信支付订单号
            String tradeNo = params.get("transaction_id");
            // 微信退款状态
            String refundStatus = params.get("refund_status");
            // 交易付款时间
            String gmtPayment = params.get("success_time");
            // 申请退款金额
            String totalAmount = params.get("refund_fee");
            if (!checkParams(outTradeNo, afterSalesId, refundStatus, gmtPayment, totalAmount, tradeNo)) {
                return "<xml>\n" +
                        "  <return_code><![CDATA[FAIL]]></return_code>\n" +
                        "  <return_msg><![CDATA[参数格式校验错误]]></return_msg>\n" +
                        "</xml>";
            }
            AfterSalesOrderInfo afterSalesOrderInfo = afterSalesOrderInfoDao.getOrderByOrderId(afterSalesId);
            OrderMasterInfo orderMasterInfo = null;
            if (afterSalesOrderInfo != null) {
                orderMasterInfo = orderMasterInfoDao.getOrderByOrderId(afterSalesOrderInfo.getOrderId());
            }
            if ("SUCCESS".equals(refundStatus)) {
                // 消息可能会多次发送，这里防止重复处理
                if (afterSalesOrderInfo != null && (AfterSalesOrderStatusEnum.RETURNING_PAID.getValue() == afterSalesOrderInfo.getStatus()
                        || AfterSalesOrderStatusEnum.RETURN_REFUND.getValue() == afterSalesOrderInfo.getStatus())) {
                    // 退款成功
                    afterSalesOrderInfoDao.setAfterSalesOrderStatus(afterSalesId, AfterSalesOrderStatusEnum.RETURNED_PAID.getValue());
                    // 记录退款负收益信息
                    userBenefitComponent.recordReFundBenefit(orderMasterInfo, afterSalesOrderInfo);
                }
            } else if ("REFUNDCLOSE".equals(refundStatus)) {
                if (afterSalesOrderInfo != null && (AfterSalesOrderStatusEnum.RETURNING_PAID.getValue() == afterSalesOrderInfo.getStatus()
                        || AfterSalesOrderStatusEnum.RETURN_REFUND.getValue() == afterSalesOrderInfo.getStatus())) {
                    afterSalesOrderInfoDao.setAfterSalesOrderStatus(afterSalesId, AfterSalesOrderStatusEnum.REFUND_CLOSE.getValue());
                }
            } else {
                if (afterSalesOrderInfo != null && (AfterSalesOrderStatusEnum.RETURNING_PAID.getValue() == afterSalesOrderInfo.getStatus()
                        || AfterSalesOrderStatusEnum.RETURN_REFUND.getValue() == afterSalesOrderInfo.getStatus())) {
                    afterSalesOrderInfoDao.setAfterSalesOrderStatus(afterSalesId, AfterSalesOrderStatusEnum.CHANGE.getValue());
                }
            }
        }
        return "<xml>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>";
    }

    @Override
    public ReFundResult reFund(String afterSalesId) throws OrderManagerException {
        AfterSalesOrderInfo afterSalesOrderInfo = afterSalesOrderInfoDao.getOrderByOrderId(afterSalesId);
        if (afterSalesOrderInfo == null) {
            throw new OrderManagerException(OrderExceptionCodeCodeEnum.ORDER_NOT_EXISTS);
        }
        if (AfterSalesOrderStatusEnum.RETURNING_PAID.getValue() == afterSalesOrderInfo.getStatus()
                || AfterSalesOrderStatusEnum.RETURN_REFUND.getValue() == afterSalesOrderInfo.getStatus()) {
            // 防止微信回调慢，该处缓存5分钟
            Boolean absent = redisUtil.setIfAbsent("reFund-" + afterSalesId, afterSalesId, 60 * 5);
            if (!absent) {
                throw new OrderManagerException(OrderExceptionCodeCodeEnum.ORDER_UNDER_DOING);
            }
            OrderMasterInfo orderMasterInfo = orderMasterInfoDao.getOrderByOrderId(afterSalesOrderInfo.getOrderId());
            if (orderMasterInfo == null) {
                throw new OrderManagerException(OrderExceptionCodeCodeEnum.ORDER_NOT_EXISTS);
            }
            ReFundResult reFundResult = new ReFundResult();
            Map<String, String> response;
            response = lpWeChatPayManager.reFund(orderMasterInfo.getId(), afterSalesId,
                    orderMasterInfo.getTotalPay().toString(), afterSalesOrderInfo.getRealReturnPay().setScale(2, BigDecimal.ROUND_HALF_UP).toString());
            reFundResult.setPaymentType(orderMasterInfo.getPaymentInfo().getPaymentType());
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            reFundResult.setGmtPayment(simpleDateFormat.format(new Date()));
            reFundResult.setTradeNo(response.get("refund_id"));
            // 将分转给元
            String totalAmount = (new BigDecimal(response.get("refund_fee"))).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP).toString();
            reFundResult.setTotalAmount(totalAmount);
            log.info("afterOrderId:{}, reFundResult:{}", afterSalesOrderInfo.getId(), JSON.toJSONString(reFundResult));
            if (hasAllReturn(orderMasterInfo, afterSalesOrderInfo)) {
                // 设置购物订单为退款状态
                orderMasterInfoDao.setAllReturnPaid(orderMasterInfo.getId(), orderMasterInfo.getPaymentInfo().getPaymentType(),
                        reFundResult.getTradeNo(), simpleDateFormat.format(new Date()), TRADE_CLOSED);
            }
            // 记录退款负收益信息,目前在回调成功后记录
//            userBenefitComponent.recordReFundBenefit(orderMasterInfo, afterSalesOrderInfo);
            return reFundResult;
        }
        return null;
    }

    @Override
    public ReFundResult reFundMock(String afterSalesId) throws OrderManagerException {
        AfterSalesOrderInfo afterSalesOrderInfo = afterSalesOrderInfoDao.getOrderByOrderId(afterSalesId);
        if (afterSalesOrderInfo == null) {
            throw new OrderManagerException(OrderExceptionCodeCodeEnum.ORDER_NOT_EXISTS);
        }
        if (AfterSalesOrderStatusEnum.RETURNING_PAID.getValue() == afterSalesOrderInfo.getStatus()
                || AfterSalesOrderStatusEnum.RETURN_REFUND.getValue() == afterSalesOrderInfo.getStatus()) {
            OrderMasterInfo orderMasterInfo = orderMasterInfoDao.getOrderByOrderId(afterSalesOrderInfo.getOrderId());
            if (orderMasterInfo == null) {
                throw new OrderManagerException(OrderExceptionCodeCodeEnum.ORDER_NOT_EXISTS);
            }
            ReFundResult reFundResult = new ReFundResult();
            reFundResult.setPaymentType(orderMasterInfo.getPaymentInfo().getPaymentType());
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            reFundResult.setGmtPayment(simpleDateFormat.format(new Date()));
            reFundResult.setTradeNo("123");
            String totalAmount = afterSalesOrderInfo.getRealReturnPay().setScale(2, BigDecimal.ROUND_HALF_UP).toString();
            reFundResult.setTotalAmount(totalAmount);
            log.info("afterOrderId:{}, reFundResult:{}", afterSalesOrderInfo.getId(), JSON.toJSONString(reFundResult));
            if (hasAllReturn(orderMasterInfo, afterSalesOrderInfo)) {
                // 设置购物订单为退款状态
                orderMasterInfoDao.setAllReturnPaid(orderMasterInfo.getId(), orderMasterInfo.getPaymentInfo().getPaymentType(),
                        reFundResult.getTradeNo(), simpleDateFormat.format(new Date()), TRADE_CLOSED);
            }
            // 记录退款负收益信息,目前在回调成功后记录
            userBenefitComponent.recordReFundBenefit(orderMasterInfo, afterSalesOrderInfo);
            return reFundResult;
        }
        return null;
    }

    /**
     * 检查订单中的商品是否都已经退完
     *
     * @param orderMasterInfo     订单
     * @param afterSalesOrderInfo 售后订单
     * @return 结果
     */
    private boolean hasAllReturn(OrderMasterInfo orderMasterInfo, AfterSalesOrderInfo afterSalesOrderInfo) {
        for (ProductInfo productInfo : orderMasterInfo.getProductInfos()) {
            Integer returnNumber = 0;
            if (productInfo.getReturnNumber() != null) {
                returnNumber = productInfo.getReturnNumber();
            }
            if (afterSalesOrderInfo.getProductInfo().getSkuId().equals(productInfo.getSkuId())) {
                if (!productInfo.getNumber().equals(afterSalesOrderInfo.getNumber() + returnNumber)) {
                    return false;
                }
            } else {
                if (!productInfo.getNumber().equals(returnNumber)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 参数非空检验
     *
     * @param strings 参数
     * @return true-合法，false-非法
     */
    private boolean checkParams(String... strings) {
        for (String string : strings) {
            if (string == null) {
                return false;
            }
        }
        return true;
    }
}
